#include "pipeline.h"

#include <gtest/gtest.h>

#include <cstdlib>
#include <iostream>
#include <sstream>

using namespace cfg;

// 目标：最小回归覆盖
// 1) 注册表查找：未知插件名 → unknown 错误
// 2) Runner 顺序：provider→transformer→freeze→validator
// 3) required_key 缺失：触发错误并包含路径与备注

TEST(PipelineTest, UnknownPluginNames)
{
    PipelineSpec spec;
    spec.providers.push_back({"no_such_provider", {}});
    spec.transformers.push_back({"no_such_transformer", {}});
    spec.validators.push_back({"no_such_validator", {}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    // 三条 unknown 错误
    ASSERT_GE(result.errors.size(), 3u);
    bool has_provider = false, has_transformer = false, has_validator = false;
    for (auto & e : result.errors)
    {
        if (e.message.find("unknown provider") != std::string::npos)
            has_provider = true;
        if (e.message.find("unknown transformer") != std::string::npos)
            has_transformer = true;
        if (e.message.find("unknown validator") != std::string::npos)
            has_validator = true;
    }
    EXPECT_TRUE(has_provider);
    EXPECT_TRUE(has_transformer);
    EXPECT_TRUE(has_validator);
}

// 测试数据文件路径辅助：返回当前测试源文件所在目录的 data/filename
static std::string
test_data_path(const char * filename)
{
    std::string file = __FILE__;
    auto pos = file.find_last_of("/\\");
    std::string dir = (pos == std::string::npos) ? std::string(".") : file.substr(0, pos);
    return dir + std::string("/data/") + filename;
}

TEST(PipelineTest, ProviderThenTransformerThenValidatorOrder)
{
    // 使用内置 single_file + normalize_keys + required_key，验证顺序起效
    PipelineSpec spec;
    spec.providers.push_back({"single_file", {{"path", test_data_path("include_value.json")}}});
    spec.transformers.push_back({"normalize_keys", {{"style", std::string("lowercase")}}});
    spec.validators.push_back({"required_key", {{"path", std::string("/value")}}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    // 应该成功冻结并得到根对象
    ASSERT_TRUE(result.root != nullptr);
    View root_view(result.root);

    // 校验路径存在（/value 在 include_value.json 中）
    auto value_view = root_view.lookup("/value");
    ASSERT_TRUE(value_view.has_value()) << "expected /value to exist";

    // 无错误
    EXPECT_TRUE(result.errors.empty());
}

TEST(PipelineTest, RequiredKeyMissing)
{
    // 从空文件开始：single_file 读取一个语法正确但不包含 /must 的输入，确保能够进入
    // validators 阶段
    PipelineSpec spec;
    spec.providers.push_back({"single_file", {{"path", test_data_path("include_value.json")}}});
    spec.transformers.push_back({"normalize_keys", {{"style", std::string("lowercase")}}});
    spec.validators.push_back({"required_key", {{"path", std::string("/must")}}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    // validators 应报告缺失键错误
    ASSERT_FALSE(result.errors.empty());
    bool has_missing = false;
    for (const auto & e : result.errors)
    {
        if (e.message.find("missing") != std::string::npos ||
            (e.notes && e.notes->find("required key missing") != std::string::npos))
        {
            has_missing = true;
            break;
        }
    }
    EXPECT_TRUE(has_missing) << "expected a required key missing diagnostic";
}

// 测试专用插件：在成功路径下产生 warn 级别诊断（Provider）
class TestWarnProvider : public IProvider
{
public:
    // 作用：模拟 provider 成功执行但产生一条非致命告警，验证流水线错误聚合逻辑。
    // 返回：始终返回 true；通过 out_errors 追加一条告警。
    bool provide(Builder & builder,
                 const PluginOptions & options,
                 std::vector<ConfigError> & out_errors) override
    {
        (void)builder;
        (void)options;
        ConfigError e{Errc::Other, "test_warn_provider: soft warning", {}};
        e.notes = "warn";
        out_errors.push_back(std::move(e));
        return true;
    }
};

REGISTER_PROVIDER("test_warn_provider", TestWarnProvider);

// 测试专用插件：在成功路径下产生 warn 级别诊断（Transformer）
class TestWarnTransformer : public ITransformer
{
public:
    // 作用：模拟 transformer 成功执行但产生一条非致命告警，验证错误聚合与跨插件隔离。
    // 返回：始终返回 true；通过 out_errors 追加一条告警。
    bool transform(Builder & builder,
                   const PluginOptions & options,
                   std::vector<ConfigError> & out_errors) override
    {
        (void)builder;
        (void)options;
        ConfigError e{Errc::Other, "test_warn_transformer: soft warning", {}};
        e.notes = "warn";
        out_errors.push_back(std::move(e));
        return true;
    }
};

REGISTER_TRANSFORMER("test_warn_transformer", TestWarnTransformer);

TEST(PipelineTest, ProviderWarningAggregated)
{
    PipelineSpec spec;
    spec.providers.push_back({"test_warn_provider", {}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    // 期望聚合到 1 条来自 provider 的告警
    size_t warn_count = 0;
    for (const auto & e : result.errors)
    {
        if (e.message.find("test_warn_provider") != std::string::npos)
            ++warn_count;
    }
    EXPECT_EQ(warn_count, 1u);
}

TEST(PipelineTest, TransformerWarningsAggregatedAndIsolated)
{
    PipelineSpec spec;
    spec.transformers.push_back({"test_warn_transformer", {}});
    spec.transformers.push_back({"test_warn_transformer", {}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    // 期望来自两个 transformer 的两条独立告警（不应丢失或相互污染）
    size_t warn_count = 0;
    for (const auto & e : result.errors)
    {
        if (e.message.find("test_warn_transformer") != std::string::npos)
            ++warn_count;
    }
    EXPECT_EQ(warn_count, 2u);
}

// 测试专用插件：在成功路径下产生 warn 级别诊断（Sink）
class TestWarnSink : public ISink
{
public:
    // 作用：模拟 sink 成功执行但产生一条非致命告警，验证错误聚合。
    // 参数：
    //  - root: 冻结后的只读根（此处不使用）
    //  - options: 插件选项（此处不使用）
    //  - out_errors: 输出过程中产生的诊断信息集合
    // 返回：始终返回 true；通过 out_errors 追加一条告警。
    bool write(const std::shared_ptr<const Object> & root,
               const PluginOptions & options,
               std::vector<ConfigError> & out_errors) override
    {
        (void)root;
        (void)options;
        ConfigError e{Errc::Other, "test_warn_sink: soft warning", {}};
        e.notes = "warn";
        out_errors.push_back(std::move(e));
        return true;
    }
};

REGISTER_SINK("test_warn_sink", TestWarnSink);

// 作用：未知 sink 名称会被 Runner 报告为错误（不会影响其他阶段）。
TEST(PipelineTest, UnknownSinkName)
{
    PipelineSpec spec;
    spec.sinks.push_back({"no_such_sink", {}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    bool has_unknown = false;
    for (const auto & e : result.errors)
    {
        if (e.message.find("unknown sink") != std::string::npos)
        {
            has_unknown = true;
            break;
        }
    }
    EXPECT_TRUE(has_unknown);
}

// 作用：sink 的无效选项应通过 PluginOptionsChecker 报告错误并被合并。
TEST(PipelineTest, SinkStdoutInvalidOptionsReportErrors)
{
    PipelineSpec spec;
    // pretty 使用无效布尔值，indent 使用非整数，都会触发 PluginOptionsChecker 的错误路径
    spec.sinks.push_back({"stdout", {{"format", "json"}, {"pretty", "maybe"}, {"indent", "abc"}}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    bool has_invalid_bool = false;
    bool has_invalid_int = false;
    for (const auto & e : result.errors)
    {
        if (e.message.find("invalid boolean") != std::string::npos)
            has_invalid_bool = true;
        if (e.message.find("invalid integer") != std::string::npos)
            has_invalid_int = true;
    }
    EXPECT_TRUE(has_invalid_bool);
    EXPECT_TRUE(has_invalid_int);
}

// 捕获 std::cout 的简单 RAII 工具，用于断言 stdout sink 的输出
class CapturedCout
{
public:
    CapturedCout() : old_buf(std::cout.rdbuf(ss.rdbuf())) {}
    ~CapturedCout() { std::cout.rdbuf(old_buf); }
    std::string str() const { return ss.str(); }

private:
    std::ostringstream ss;
    std::streambuf * old_buf;
};

// 复杂对象 provider：构造包含嵌套对象/数组、转义字符与 null 的样例
class TestComplexProvider : public IProvider
{
public:
    bool provide(Builder & builder,
                 const PluginOptions & options,
                 std::vector<ConfigError> & out_errors) override
    {
        (void)options;
        (void)out_errors;
        // 排序可验证：x<y<z
        builder.set("a.z", int64_t{1});
        builder.set("a.y", int64_t{2});
        builder.set("a.x", int64_t{3});
        // 数组含转义与 null
        builder.set("arr[0]", std::string("line1\nline2"));
        builder.set("arr[1]", std::string("quote:\" backslash:\\ tab:\t ctrl:\x01"));
        builder.set("arr[2]", nullptr);
        builder.set("arr[3].k", std::string("v"));
        // 嵌套数组+对象（使用 JSON Pointer）
        builder.set_pointer("/nested/0/obj/key", std::string("val"));
        // 顶层空值与数字
        builder.set("n", nullptr);
        builder.set("num", 3.14);
        builder.set("s", std::string("hello"));
        return true;
    }
};

REGISTER_PROVIDER("test_complex_provider", TestComplexProvider);

// 紧凑模式：验证转义、排序、null 与整体结构的精确输出（末尾包含换行）
TEST(PipelineTest, SinkStdoutComplexObjectCompact)
{
    PipelineSpec spec;
    spec.providers.push_back({"test_complex_provider", {}});
    spec.sinks.push_back(
        {"stdout", {{"format", "json"}, {"pretty", "false"}, {"newline", "true"}}});

    CapturedCout capture;
    PipelineRunner runner;
    auto result = runner.run(spec);

    ASSERT_TRUE(result.errors.empty());
    std::string out = capture.str();

    // 期望的紧凑 JSON（键名按字典序排序）
    const std::string expected = "{\"a\":{\"x\":3,\"y\":2,\"z\":1},\"arr\":[\"line1\\nline2\","
                                 "\"quote:\\\" backslash:\\\\ tab:\\t "
                                 "ctrl:\\u0001\",null,{\"k\":\"v\"}],\"n\":null,\"nested\":[{"
                                 "\"obj\":{\"key\":\"val\"}}],\"num\":3.14,\"s\":\"hello\"}\n";

    EXPECT_EQ(out, expected);
}

// 美化模式：验证缩进与关键片段的顺序与转义
TEST(PipelineTest, SinkStdoutComplexObjectPretty)
{
    PipelineSpec spec;
    spec.providers.push_back({"test_complex_provider", {}});
    spec.sinks.push_back(
        {"stdout", {{"format", "json"}, {"pretty", "true"}, {"indent", "2"}, {"newline", "true"}}});

    CapturedCout capture;
    PipelineRunner runner;
    auto result = runner.run(spec);

    ASSERT_TRUE(result.errors.empty());
    std::string out = capture.str();
    ASSERT_FALSE(out.empty());
    EXPECT_EQ(out.back(), '\n');

    // 根级键顺序检查（按字典序）：a -> arr -> n -> nested -> num -> s
    auto pos_a = out.find("\n  \"a\": {");
    auto pos_arr = out.find("\n  \"arr\": [", pos_a);
    auto pos_n = out.find("\n  \"n\": null", pos_arr);
    auto pos_nested = out.find("\n  \"nested\": [", pos_n);
    auto pos_num = out.find("\n  \"num\": ", pos_nested);
    auto pos_s = out.find("\n  \"s\": \"hello\"", pos_num);
    ASSERT_NE(pos_a, std::string::npos);
    ASSERT_NE(pos_arr, std::string::npos);
    ASSERT_NE(pos_n, std::string::npos);
    ASSERT_NE(pos_nested, std::string::npos);
    ASSERT_NE(pos_num, std::string::npos);
    ASSERT_NE(pos_s, std::string::npos);
    EXPECT_LT(pos_a, pos_arr);
    EXPECT_LT(pos_arr, pos_n);
    EXPECT_LT(pos_n, pos_nested);
    EXPECT_LT(pos_nested, pos_num);
    EXPECT_LT(pos_num, pos_s);

    // 子对象 a 的缩进与内容
    EXPECT_NE(out.find("\n    \"x\": 3,"), std::string::npos);
    EXPECT_NE(out.find("\n    \"y\": 2,"), std::string::npos);
    EXPECT_NE(out.find("\n    \"z\": 1\n  }"), std::string::npos);

    // 数组元素转义/缩进
    EXPECT_NE(out.find("\n    \"line1\\nline2\","), std::string::npos);
    EXPECT_NE(out.find("\n    \"quote:\\\" backslash:\\\\ tab:\\t ctrl:\\u0001\","),
              std::string::npos);
    EXPECT_NE(out.find("\n    null"), std::string::npos);
    EXPECT_NE(out.find("\n    {\n      \"k\": \"v\"\n    }\n  ]"), std::string::npos);
}

// 插值测试专用 provider：设置基于环境变量的占位符与默认值
class TestInterpolateEnvProvider : public IProvider
{
public:
    bool provide(Builder & builder,
                 const PluginOptions & options,
                 std::vector<ConfigError> & out_errors) override
    {
        (void)options;
        (void)out_errors;
        builder.set("x1", std::string("${env:CFG_TEST_VAR}"));
        builder.set("x2", std::string("${env:CFG_TEST_MISSING:default_fallback}"));
        return true;
    }
};

REGISTER_PROVIDER("test_interpolate_env_provider", TestInterpolateEnvProvider);

// 插值测试专用 provider：设置配置路径引用与数组索引引用场景
class TestInterpolatePathProvider : public IProvider
{
public:
    bool provide(Builder & builder,
                 const PluginOptions & options,
                 std::vector<ConfigError> & out_errors) override
    {
        (void)options;
        (void)out_errors;
        builder.set("db.host", std::string("localhost"));
        builder.set("db.port", int64_t{3306});
        builder.set("arr[0]", std::string("a"));
        builder.set("arr[1]", std::string("b"));
        builder.set("demo.absolute", std::string("${db.host}"));
        builder.set("demo.json_pointer", std::string("${/db/port}"));
        builder.set("demo.array_index", std::string("${arr[1]}"));
        builder.set("demo.defaulted", std::string("${missing.path:default_value}"));
        return true;
    }
};

REGISTER_PROVIDER("test_interpolate_path_provider", TestInterpolatePathProvider);

// 插值测试专用 provider：设置三元表达式与默认分支的占位符
class TestInterpolateTernaryProvider : public IProvider
{
public:
    bool provide(Builder & builder,
                 const PluginOptions & options,
                 std::vector<ConfigError> & out_errors) override
    {
        (void)options;
        (void)out_errors;
        builder.set("db.host", std::string("h.example"));
        builder.set("choose_env", std::string("${env:CFG_TEST_FLAG?on:off}"));
        builder.set("choose_path", std::string("${db.missing?have:miss}"));
        builder.set("choose_nested", std::string("${db.host?${db.host}:none}"));
        return true;
    }
};

REGISTER_PROVIDER("test_interpolate_ternary_provider", TestInterpolateTernaryProvider);

// 插值测试专用 provider：制造循环引用以验证错误报告
class TestInterpolateCircularProvider : public IProvider
{
public:
    bool provide(Builder & builder,
                 const PluginOptions & options,
                 std::vector<ConfigError> & out_errors) override
    {
        (void)options;
        (void)out_errors;
        builder.set("a", std::string("${b}"));
        builder.set("b", std::string("${a}"));
        return true;
    }
};

REGISTER_PROVIDER("test_interpolate_circular_provider", TestInterpolateCircularProvider);

// 插值测试专用 provider：类型转换为字符串（数字/布尔/null）
class TestInterpolateTypeConversionProvider : public IProvider
{
public:
    bool provide(Builder & builder,
                 const PluginOptions & options,
                 std::vector<ConfigError> & out_errors) override
    {
        (void)options;
        (void)out_errors;
        builder.set("num", int64_t{42});
        builder.set("flag", true);
        builder.set("n", nullptr);
        builder.set("s_num", std::string("${num}"));
        builder.set("s_flag", std::string("${flag}"));
        builder.set("s_null", std::string("${n}"));
        return true;
    }
};

REGISTER_PROVIDER("test_interpolate_type_conversion_provider",
                  TestInterpolateTypeConversionProvider);

// 作用：验证环境变量插值在存在与不存在时分别取实际值与默认值
TEST(InterpolateTest, EnvVarWithAndWithoutDefault)
{
    // 设置环境变量供测试
    ::setenv("CFG_TEST_VAR", "xyz", 1);
    ::unsetenv("CFG_TEST_MISSING");

    PipelineSpec spec;
    spec.providers.push_back({"test_interpolate_env_provider", {}});
    spec.transformers.push_back({"interpolate", {}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    ASSERT_TRUE(result.root != nullptr);
    View root_view(result.root);
    auto v1 = root_view.lookup("x1");
    auto v2 = root_view.lookup("x2");
    ASSERT_TRUE(v1.has_value());
    ASSERT_TRUE(v2.has_value());
    auto s1 = v1->get_string();
    auto s2 = v2->get_string();
    ASSERT_TRUE(s1.has_value());
    ASSERT_TRUE(s2.has_value());
    EXPECT_EQ(*s1, "xyz");
    EXPECT_EQ(*s2, "default_fallback");
}

// 作用：验证配置路径与数组索引插值、JSON Pointer 与默认值行为
TEST(InterpolateTest, ConfigPathArrayIndexAndDefaults)
{
    PipelineSpec spec;
    spec.providers.push_back({"test_interpolate_path_provider", {}});
    spec.transformers.push_back({"interpolate", {}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    ASSERT_TRUE(result.root != nullptr);
    View root_view(result.root);

    auto abs_v = root_view.lookup("demo.absolute");
    auto json_ptr_v = root_view.lookup("demo.json_pointer");
    auto arr_idx_v = root_view.lookup("demo.array_index");
    auto defaulted_v = root_view.lookup("demo.defaulted");

    ASSERT_TRUE(abs_v.has_value());
    ASSERT_TRUE(json_ptr_v.has_value());
    ASSERT_TRUE(arr_idx_v.has_value());
    ASSERT_TRUE(defaulted_v.has_value());

    auto s_abs = abs_v->get_string();
    auto s_json_ptr = json_ptr_v->get_string();
    auto s_arr_idx = arr_idx_v->get_string();
    auto s_defaulted = defaulted_v->get_string();

    ASSERT_TRUE(s_abs.has_value());
    ASSERT_TRUE(s_json_ptr.has_value());
    ASSERT_TRUE(s_arr_idx.has_value());
    ASSERT_TRUE(s_defaulted.has_value());

    EXPECT_EQ(*s_abs, "localhost");
    EXPECT_EQ(*s_json_ptr, "3306");
    EXPECT_EQ(*s_arr_idx, "b");
    EXPECT_EQ(*s_defaulted, "default_value");
}

// 作用：验证三元表达式在环境变量与路径条件下的分支选择与嵌套插值
TEST(InterpolateTest, TernaryEnvAndPathConditions)
{
    // 环境变量条件为真
    ::setenv("CFG_TEST_FLAG", "1", 1);

    PipelineSpec spec;
    spec.providers.push_back({"test_interpolate_ternary_provider", {}});
    spec.transformers.push_back({"interpolate", {}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    ASSERT_TRUE(result.root != nullptr);
    View root_view(result.root);

    auto choose_env_v = root_view.lookup("choose_env");
    auto choose_path_v = root_view.lookup("choose_path");
    auto choose_nested_v = root_view.lookup("choose_nested");

    ASSERT_TRUE(choose_env_v.has_value());
    ASSERT_TRUE(choose_path_v.has_value());
    ASSERT_TRUE(choose_nested_v.has_value());

    auto s_env = choose_env_v->get_string();
    auto s_path = choose_path_v->get_string();
    auto s_nested = choose_nested_v->get_string();

    ASSERT_TRUE(s_env.has_value());
    ASSERT_TRUE(s_path.has_value());
    ASSERT_TRUE(s_nested.has_value());

    EXPECT_EQ(*s_env, "on");
    EXPECT_EQ(*s_path, "miss");
    EXPECT_EQ(*s_nested, "h.example");

    // 清理环境变量，避免影响其他测试
    ::unsetenv("CFG_TEST_FLAG");
}

// 作用：验证循环引用会被检测并产生错误报告
TEST(InterpolateTest, CircularReferenceReportsError)
{
    PipelineSpec spec;
    spec.providers.push_back({"test_interpolate_circular_provider", {}});
    spec.transformers.push_back({"interpolate", {}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    bool has_circular_error = false;
    for (const auto & e : result.errors)
    {
        if (e.message.find("circular reference detected") != std::string::npos)
        {
            has_circular_error = true;
            break;
        }
    }
    EXPECT_TRUE(has_circular_error);
}

// 作用：验证数值与布尔值会被转换为字符串，null 转换为空串
TEST(InterpolateTest, ValueToStringConversions)
{
    PipelineSpec spec;
    spec.providers.push_back({"test_interpolate_type_conversion_provider", {}});
    spec.transformers.push_back({"interpolate", {}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    ASSERT_TRUE(result.root != nullptr);
    View root_view(result.root);

    auto s_num_v = root_view.lookup("s_num");
    auto s_flag_v = root_view.lookup("s_flag");
    auto s_null_v = root_view.lookup("s_null");

    ASSERT_TRUE(s_num_v.has_value());
    ASSERT_TRUE(s_flag_v.has_value());
    ASSERT_TRUE(s_null_v.has_value());

    auto s_num = s_num_v->get_string();
    auto s_flag = s_flag_v->get_string();
    auto s_null = s_null_v->get_string();

    ASSERT_TRUE(s_num.has_value());
    ASSERT_TRUE(s_flag.has_value());
    ASSERT_TRUE(s_null.has_value());

    EXPECT_EQ(*s_num, "42");
    EXPECT_EQ(*s_flag, "true");
    EXPECT_EQ(*s_null, "");
}

// 测试专用插件：用于在 transformers 阶段之后写入若干带有混合键风格的键值，便于验证 normalize_keys
// 的效果。
class TestSetKeysTransformer : public ITransformer
{
public:
    // 在 builder 上写入多种风格的键，覆盖驼峰、全大写缩写、连字符、空格与已是下划线的情况。
    bool transform(Builder & builder,
                   const PluginOptions & options,
                   std::vector<ConfigError> & out_errors) override
    {
        (void)options;
        (void)out_errors;
        builder.set("DbHost", std::string("localhost"));
        builder.set("HTTPServer", std::string("ok"));
        builder.set("userID", int64_t{1});
        builder.set("user-ID2", int64_t{2});
        builder.set("x y z", std::string("space"));
        builder.set("already_snake", std::string("yes"));
        return true;
    }
};

REGISTER_TRANSFORMER("test_set_keys_transformer", TestSetKeysTransformer);

// 说明：normalize_keys(style=snake_case) 应影响其后的 transformers 对 builder 的写入结果。
TEST(NormalizeKeysTest, SnakeCaseAffectsLaterTransformers)
{
    PipelineSpec spec;
    spec.transformers.push_back({"normalize_keys", {{"style", "snake_case"}}});
    spec.transformers.push_back({"test_set_keys_transformer", {}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    ASSERT_TRUE(result.root != nullptr);
    View v(result.root);

    // 期望全部被转换为 snake_case
    EXPECT_TRUE(v.lookup("db_host").has_value());
    EXPECT_TRUE(v.lookup("h_t_t_p_server").has_value());
    EXPECT_TRUE(v.lookup("user_id").has_value());
    EXPECT_TRUE(v.lookup("user_id2").has_value());
    EXPECT_TRUE(v.lookup("x_y_z").has_value());
    EXPECT_TRUE(v.lookup("already_snake").has_value());

    // 非 snake_case 原键不应存在
    EXPECT_FALSE(v.lookup("DbHost").has_value());
    EXPECT_FALSE(v.lookup("HTTPServer").has_value());
    EXPECT_FALSE(v.lookup("userID").has_value());
    EXPECT_FALSE(v.lookup("user-ID2").has_value());
    EXPECT_FALSE(v.lookup("x y z").has_value());
}

// 说明：normalize_keys(style=非法值) 应报告一条诊断，并回退为 lowercase；其后的写入按小写映射。
TEST(NormalizeKeysTest, InvalidStyleReportsErrorAndLowercaseFallback)
{
    PipelineSpec spec;
    spec.transformers.push_back({"normalize_keys", {{"style", "kebab-case"}}});
    spec.transformers.push_back({"test_set_keys_transformer", {}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    ASSERT_TRUE(result.root != nullptr);
    View v(result.root);

    // 回退为 lowercase：仅做小写转换，不进行下划线插入/合并
    EXPECT_TRUE(v.lookup("dbhost").has_value());     // DbHost -> dbhost
    EXPECT_TRUE(v.lookup("httpserver").has_value()); // HTTPServer -> httpserver
    EXPECT_TRUE(v.lookup("userid").has_value());     // userID -> userid
    EXPECT_TRUE(v.lookup("user-id2").has_value());   // 连字符保留，仅小写
    EXPECT_TRUE(v.lookup("x y z").has_value());      // 空格保留，仅小写
    EXPECT_TRUE(
        v.lookup("already_snake").has_value()); // 已为下划线保持不变（仅小写，本例已是小写）

    bool has_invalid_style = false;
    bool has_fallback_note = false;
    for (const auto & e : result.errors)
    {
        if (e.message.find("normalize_keys: invalid style") != std::string::npos)
        {
            has_invalid_style = true;
            if (e.notes && e.notes->find("fallback to lowercase") != std::string::npos)
                has_fallback_note = true;
        }
    }
    EXPECT_TRUE(has_invalid_style);
    EXPECT_TRUE(has_fallback_note);
}

// 说明：single_file provider 缺失必填选项 path 时，应报告明确错误，且不应崩溃。
TEST(ProviderSingleFileTest, MissingPathOptionReportsError)
{
    PipelineSpec spec;
    spec.providers.push_back({"single_file", {}}); // 缺失 path

    PipelineRunner runner;
    auto result = runner.run(spec);

    bool found_error = false;
    for (const auto & e : result.errors)
    {
        if (e.message.find("single_file: missing option 'path'") != std::string::npos)
        {
            found_error = true;
            break;
        }
    }
    EXPECT_TRUE(found_error);
}

// 说明：required_key 在 JSON Pointer 缺失时，应补充基于 pointer 的路径切分 tokens。
TEST(ValidatorRequiredKeyTest, JsonPointerMissingProducesTokenizedPath)
{
    PipelineSpec spec;
    spec.validators.push_back({"required_key", {{"path", "/a/b/c"}}});

    PipelineRunner runner;
    auto result = runner.run(spec);

    bool matched = false;
    for (const auto & e : result.errors)
    {
        if (e.notes && e.notes->find("required key missing") != std::string::npos)
        {
            matched = true;
            ASSERT_EQ(e.path.size(), 3u);
            EXPECT_EQ(e.path[0], "a");
            EXPECT_EQ(e.path[1], "b");
            EXPECT_EQ(e.path[2], "c");
            break;
        }
    }
    EXPECT_TRUE(matched);
}

// ... existing code ...
